﻿
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace HmFramework.Threading
{

	/// <summary>
	/// 读写资源的令牌
	/// </summary>
	public abstract class LockToken<T> : IDisposable
	{

		public LockableObjectContainer<T> _container;

		/// <summary>
		/// 被锁住的资源引用
		/// </summary>
		public T Value { get; set; }

		/// <summary>
		/// 读写锁的原钥匙，对其销毁即为解锁
		/// </summary>
		protected IDisposable _innerTicket;

		/// <summary>
		/// 创建读写资源的令牌
		/// </summary>
		/// <param name="value">解锁的资源引用</param>
		/// <param name="innerTicket">锁的真实引用</param>
		/// <param name="beforeDispose">在解锁前需要做的操作 如把资源的新值覆盖回锁住的资源</param>
		/// <param name="afterDispose">在解锁后需要做的操作 如写日志</param>
		internal LockToken(LockableObjectContainer<T> container, IDisposable innerTicket, Action<LockToken<T>> beforeDispose, Action<LockToken<T>> afterDispose)
		{
			_container = container;
			_innerTicket = innerTicket;
			Value = container._value;
			this._disposeAction =
					() =>
					{

						try
						{
							if (beforeDispose != null) beforeDispose(this);
							this.Value = default(T);
							_innerTicket.Dispose();
							if (afterDispose != null) afterDispose(this);
						}
						catch
						{
						}
					};
		}

		#region IDisposable Members

		/// <summary>
		/// 令牌销毁时要做的操作
		/// </summary>
		Action _disposeAction;

		/// <summary>
		/// 销毁
		/// </summary>
		public void Dispose()
		{
			_disposeAction();
		}

		#endregion

	}

	public class ReadLockToken<T> : LockToken<T>
	{

		public ReadLockToken(LockableObjectContainer<T> container, ReaderWriterLockSlim _lock) :
			base(container, _lock.CreateLockScope(LockType.Read), null, null)
		{
		}
	}

	public class WriteLockToken<T> : LockToken<T>
	{

		public WriteLockToken(LockableObjectContainer<T> container, ReaderWriterLockSlim _lock)
			: base
			(
				container,
				_lock.CreateLockScope(LockType.Write),
				lt =>
				{
					//在解锁前 把新值保存给原对象 
					lt._container._value = lt.Value;
				},
				null
			)
		{
		}
	}
}
